﻿/************************************************************************
* Copyright (c) 2018 All Rights Reserved.
*命名空间：Wx.Sdk.Pay.Src
*文件名： PayService
*创建人： kingling
*创建时间：2018/10/6 17:50:43
*描述
*=======================================================================
*修改标记
*修改时间：2018/10/6 17:50:43
*修改人：kingling
*描述：
************************************************************************/

using Microsoft.AspNetCore.Http;
using System;
using System.Threading.Tasks;
using Wx.Sdk.Ext;
using Wx.Sdk.Pay.Entity;
using Wx.Sdk.Pay.Enums;
using Wx.Sdk.Utils;

namespace Wx.Sdk.Pay.Src
{
    public class PayOrder : IPayOrder
    {
        WxPay wxPay;
        /// <summary>
        /// 统一下单地址，除被扫支付场景以外，商户系统先调用该接口在微信支付服务后台生成预支付交易单，返回正确的预支付交易回话标识后再按扫码、JSAPI、APP等不同场景生成交易串调起支付。
        /// </summary>
        private static readonly string unifiedorderurl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        /// <summary>
        /// 退款地址
        /// </summary>
        private static readonly string refundurl = "https://api.mch.weixin.qq.com/secapi/pay/refund";
        /// <summary>
        /// 企业付款地址
        /// </summary>
        private static readonly string paymentUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
        /// <summary>
        /// 普通红包地址
        /// </summary>
        private static readonly string normalRedParkUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
        /// <summary>
        /// 裂变红包地址
        /// </summary>
        private static readonly string groupRedParkUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack";
        HttpContext context;
        public PayOrder(WxPay wxPay)
        {
            this.wxPay = wxPay;
            context = wxPay.httpContextAccessor.HttpContext;
        }
        async Task<string> IPayOrder.QRCodePay(decimal orderMoney, string orderNo, string body, string productId)
        {
            return await UnifiedOrder(orderMoney, orderNo, body, EPay.NATIVE, "", productId);
        }
        async Task<string> IPayOrder.H5Pay(decimal orderMoney, string orderNo, string body, string spbill_create_ip)
        {
            return await UnifiedOrder(orderMoney, orderNo, body, EPay.MWEB, "", "", spbill_create_ip: spbill_create_ip);
        }
        async Task<JsPayInfo> IPayOrder.JsPay(decimal orderMoney, string orderNo, string body, string openid)
        {
            var prepay_id = await UnifiedOrder(orderMoney, orderNo, body, EPay.JSAPI, openid, "");
            var payinfo = new JsPayInfo
            {
                appId = wxPay.payOption.appid,
                nonceStr = StringHelper.GetRandomString(16),
                timeStamp = TimeHelper.ToUnix().ToString(),
                package = $"prepay_id={prepay_id}",
            };
            //参数给完 开始签名
            var str = ModelHelper.ToUrlParameter(payinfo) + $"&key={wxPay.payOption.sign_key}";
            payinfo.paySign = payinfo.signType.ToUpper() == "MD5" ? EncyptHelper.ToMD5(str, false) : EncyptHelper.ToHMACSHA256(str, wxPay.payOption.sign_key);
            return payinfo;
        }

        async Task<AppPayInfo> IPayOrder.AppPay(decimal orderMoney, string orderNo, string body)
        {
            var prepay_id = await UnifiedOrder(orderMoney, orderNo, body, EPay.APP, "", "");
            var payinfo = new AppPayInfo
            {
                appid = wxPay.payOption.appid,
                noncestr = StringHelper.GetRandomString(16),
                timestamp = TimeHelper.ToUnix().ToString(),
                partnerid = wxPay.payOption.mchid,
                prepayid = prepay_id,
            };
            //参数给完 开始签名
            var str = ModelHelper.ToUrlParameter(payinfo) + $"&key={wxPay.payOption.sign_key}";
            payinfo.sign = payinfo.signType.ToUpper() == "MD5" ? EncyptHelper.ToMD5(str, false) : EncyptHelper.ToHMACSHA256(str, wxPay.payOption.sign_key);
            return payinfo;
        }

        async Task<bool> IPayOrder.Refund(string orderNo, string tradeNo, decimal refundMoney, decimal totalMoney)
        {
            var order = new Refund
            {
                out_trade_no = tradeNo,
                out_refund_no = orderNo,
                total_fee = (int)(totalMoney * 100),
                refund_fee = (int)(refundMoney * 100)
            };
            order.appid = wxPay.payOption.appid;
            order.mch_id = wxPay.payOption.mchid;
            order.sign_type = wxPay.payOption.sign_type;
            order.notify_url = wxPay.payOption.notify_url + wxPay.payOption.refund_notify;
            order.nonce_str = StringHelper.GetRandomString(16);
            //参数给完 开始签名
            var str = ModelHelper.ToUrlParameter(order) + $"&key={wxPay.payOption.sign_key}";
            order.sign = wxPay.payOption.sign_type.ToUpper() == "MD5" ? EncyptHelper.ToMD5(str, false) : EncyptHelper.ToHMACSHA256(str, wxPay.payOption.sign_key);
            //开始组装xml字符串
            var xml = XmlHelper.ToXml(order);
            //传值
            var response = await wxPay.payBase.Post(xml, refundurl, true);

            var result = XmlHelper.ToObject<UnifiedOrderReturn>(response);
            //开始判断
            if (result.return_code != "SUCCESS")
            {
                throw new WxError(result.return_msg);
            }
            if (result.result_code != "SUCCESS")
            {
                throw new WxError($"错误代码:{result.err_code},错误描述:{result.err_code_des}");
            }
            return true;
        }
        async Task<bool> IPayOrder.Payment(string orderNo, string openid, decimal amount, string remark, bool checkName, string userName)
        {
            var order = new Payment
            {
                partner_trade_no = orderNo,
                amount = (int)(amount * 100),
                openid = openid,
                desc = remark,
                check_name = checkName ? "FORCE_CHECK" : "NO_CHECK",
                re_user_name = userName,
                spbill_create_ip = "127.0.0.1",
                device_info = "default"
            };
            order.mch_appid = wxPay.payOption.appid;
            order.mchid = wxPay.payOption.mchid;
            //order.sign_type = wxPay.payOption.sign_type;
            order.nonce_str = StringHelper.GetRandomString(16);
            //参数给完 开始签名
            var str = ModelHelper.ToUrlParameter(order) + $"&key={wxPay.payOption.sign_key}";
            order.sign = EncyptHelper.ToMD5(str, false);
            //开始组装xml字符串
            var xml = XmlHelper.ToXml(order);
            //传值
            var response = await wxPay.payBase.Post(xml, paymentUrl, true);

            var result = XmlHelper.ToObject<UnifiedOrderReturn>(response);
            //开始判断
            if (result.return_code != "SUCCESS")
            {
                throw new WxError(result.return_msg);
            }
            if (result.result_code != "SUCCESS")
            {
                throw new WxError($"错误代码:{result.err_code},错误描述:{result.err_code_des}");
            }
            return true;
        }
        async Task<bool> IPayOrder.NormalRedPark(string orderNo, string openid, decimal amount, string sendName, string actName, string wishing, string remark, RedPackEnum redPack, string consumeMchId)
        {
            var order = new RedPack
            {
                mch_billno = orderNo,
                total_amount = (int)(amount * 100),
                re_openid = openid,
                remark = remark,
                send_name = sendName,
                act_name = actName,
                client_ip = "127.0.0.1",
                wishing = wishing,
                total_num = 1,
                scene_id = redPack.ToString(),
                consume_mch_id = consumeMchId,
            };
            return await SendRedPark(order, normalRedParkUrl);
        }

        async Task<bool> IPayOrder.GroupRedPark(string orderNo, string openid, decimal amount, int totalNum, string sendName, string actName, string wishing, string remark, RedPackEnum redPack, string consumeMchId)
        {
            if (totalNum < 3 || totalNum > 20)
            {
                throw new WxError("裂变红包发放人数在3到20之间");
            }
            var order = new RedPack
            {
                mch_billno = orderNo,
                total_amount = (int)(amount * 100),
                re_openid = openid,
                remark = remark,
                send_name = sendName,
                act_name = actName,
                client_ip = "127.0.0.1",
                wishing = wishing,
                total_num = totalNum,
                scene_id = redPack.ToString(),
                consume_mch_id = consumeMchId,
            };
            return await SendRedPark(order, groupRedParkUrl);
        }
        #region 统一下单
        private async Task<string> UnifiedOrder(decimal orderMoney, string orderNo, string body, EPay ePay, string openid, string productId,string spbill_create_ip="127.0.0.1")
        {
            var order = new UnifiedOrder
            {
                body = body,
                out_trade_no = orderNo,
                trade_type = ePay.ToString("f"),
                spbill_create_ip = spbill_create_ip,
                total_fee = (int)(orderMoney * 100),
                product_id = productId,
                openid = openid,
            };
            order.appid = wxPay.payOption.appid;
            order.mch_id = wxPay.payOption.mchid;
            order.sign_type = wxPay.payOption.sign_type;
            order.notify_url = wxPay.payOption.notify_url + wxPay.payOption.pay_notify;
            order.nonce_str = StringHelper.GetRandomString(16);
            //参数给完 开始签名
            var str = ModelHelper.ToUrlParameter(order) + $"&key={wxPay.payOption.sign_key}";
            if (order.sign_type.ToLower() == "md5")
            {
                order.sign = EncyptHelper.ToMD5(str).ToUpper();
            }
            else
            {
                order.sign = EncyptHelper.ToHMACSHA256(str, wxPay.payOption.sign_key);
            }
            //检测必填参数
            if (string.IsNullOrWhiteSpace(order.out_trade_no))
            {
                throw new WxError("缺少统一支付接口必填参数out_trade_no！");
            }
            else if (string.IsNullOrWhiteSpace(order.body))
            {
                throw new WxError("缺少统一支付接口必填参数body！");
            }
            else if (order.total_fee <= 0)
            {
                throw new WxError("缺少统一支付接口必填参数total_fee！");
            }
            else if (string.IsNullOrWhiteSpace(order.trade_type))
            {
                throw new WxError("缺少统一支付接口必填参数trade_type！");
            }

            //关联参数
            if (order.trade_type == "JSAPI" && string.IsNullOrWhiteSpace(order.openid))
            {
                throw new WxError("统一支付接口中，缺少必填参数openid！trade_type为JSAPI时，openid为必填参数！");
            }
            if (order.trade_type.ToString() == "NATIVE" && string.IsNullOrWhiteSpace(order.product_id))
            {
                throw new WxError("统一支付接口中，缺少必填参数product_id！trade_type为JSAPI时，product_id为必填参数！");
            }

            //异步通知url未设置，则使用配置文件中的url
            if (string.IsNullOrWhiteSpace(order.notify_url))
            {
                throw new WxError("统一支付接口中，notify_url");
            }
            //开始组装xml字符串
            var xml = XmlHelper.ToXml(order);
            //传值
            var response = await wxPay.payBase.Post(xml, unifiedorderurl);
            var result = XmlHelper.ToObject<UnifiedOrderReturn>(response);
            //开始判断
            if (result.return_code != "SUCCESS")
            {
                throw new WxError(result.return_msg);

            }
            if (result.result_code != "SUCCESS")
            {
                throw new WxError($"错误代码:{result.err_code},错误描述:{result.err_code_des}");
            }

            //---业务返回,NATIVE时才返回二维码地址
            if (result.trade_type == EPay.NATIVE.ToString())
            {
                return result.code_url;
            }
            if (result.trade_type == EPay.MWEB.ToString())
            {
                return result.mweb_url;
            }
            return result.prepay_id;
        }
        #endregion
        #region 红包
        private async Task<bool> SendRedPark(RedPack order, string url)
        {
            order.wxappid = wxPay.payOption.appid;
            order.mch_id = wxPay.payOption.mchid;
            //order.sign_type = wxPay.payOption.sign_type;
            order.nonce_str = StringHelper.GetRandomString(16);
            //参数给完 开始签名
            var str = ModelHelper.ToUrlParameter(order) + $"&key={wxPay.payOption.sign_key}";
            order.sign = EncyptHelper.ToMD5(str, false);
            //开始组装xml字符串
            var xml = XmlHelper.ToXml(order);
            //传值
            var response = await wxPay.payBase.Post(xml, url, true);
            var result = XmlHelper.ToObject<UnifiedOrderReturn>(response);
            //开始判断
            if (result.return_code != "SUCCESS")
            {
                throw new WxError(result.return_msg);
            }
            if (result.result_code != "SUCCESS")
            {
                throw new WxError($"错误代码:{result.err_code},错误描述:{result.err_code_des}");
            }
            return true;
        }
        #endregion
    }
}
